package start

import (
	"context"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"gitee.com/go-course/go11/vblog/conf"
	"gitee.com/go-course/go11/vblog/ioc"
	"gitee.com/go-course/go11/vblog/logger"
	"gitee.com/go-course/go11/vblog/protocol"
	"github.com/gin-gonic/gin"
	libioc "github.com/infraboard/mcube/ioc"
	"github.com/spf13/cobra"

	// 加载所有的业务实现和接口实现, init
	// 统一到apps包下面, 然后只导入一次,
	// apps: 代表当前已经开发完成的业务功能实例和接口实例
	// 把所有的业务模块全部加载
	_ "gitee.com/go-course/go11/vblog/apps"
	// 可以直接导入
	// // 注册api handler
	// _ "gitee.com/go-course/go11/vblog/apps/blog/api"
	// // 注册 blog 业务具体实现MySQL
	// _ "gitee.com/go-course/go11/vblog/apps/blog/impl"
)

var (
	configType string
	configFile string
)

// root command
// vblog-api start
var Cmd = &cobra.Command{
	Use:   "start",
	Short: "vblog项目后端",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		// 命令具体的执行逻辑
		// vblog-api start xxx xxx xxx
		// [xxx xxx xxx]

		// 程序的组装

		// 1. 加载全局配置
		switch configType {
		case "env":
			_, err := conf.LoadConfigFromEnv()
			cobra.CheckErr(err)
		default:
			// 默认通过配置文件加载
			_, err := conf.LoadConfigFromToml(configFile)
			cobra.CheckErr(err)
		}

		// 加载业务逻辑实现
		r := gin.Default()
		logger.L().Debug().Msgf("controllers: %s", ioc.ShowConntrollers())
		logger.L().Debug().Msgf("apis: %s", ioc.ShowApis())

		err := ioc.InitController()
		cobra.CheckErr(err)
		err = ioc.InitHttpApi("/vblog/api/v1", r)
		cobra.CheckErr(err)

		// 启动Http协议服务器
		httpServer := protocol.NewHttp(r)
		go func() {
			herr := httpServer.Start()
			if herr != http.ErrServerClosed {
				cobra.CheckErr(herr)
			}
		}()
		//  启动GRPC server
		grpcServer := protocol.NewGrpc()
		go func() {
			gerr := grpcServer.Start()
			cobra.CheckErr(gerr)
		}()

		// 如果进程退出了，是没有资源是否逻辑:
		// 1. 正在处理的请求 会直接失败
		// 2. 数据库或者一些事务的状态会出现异常
		// 3. 一些打开的文件 需要关闭
		// 4. 一些正在消费的队列(kafka) 停止处理
		// v3.0版本
		err = libioc.InitIocObject()
		cobra.CheckErr(err)

		// 信号处理 负责阻塞住主进行(Loop Os Signal)
		// 处理信号量
		ch := make(chan os.Signal, 1)
		// 如果os发出了信号后，会把该信号发往 ch channal
		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT)

		// 从chanel中获取信号
		s := <-ch
		logger.L().Info().Msgf("receive os signal: %s, exit ...", s)

		// 做后续处理
		ctx := context.Background()
		httpServer.Stop(ctx)
		grpcServer.Stop(ctx)
		// 其他资源释放逻辑
		conf.C().MySQL.Close()
	},
}

func init() {
	// Flag --config-type -t file -f etc/config.toml
	Cmd.Flags().StringVarP(&configType, "config-type", "t", "file", "程序加载配置的方式")
	Cmd.Flags().StringVarP(&configFile, "config-file", "f", "etc/config.toml", "配置文件的路径")
}
